#include "simulation.h"

const string ctrl_names[] =
{"",
 "casovany kontroler",
 "kontroler s prodluzovanim faze zelene",
 "fuzzy kontroler"
};

Simulation::Simulation(string sim_name, int controller_type):
	simulation_name(sim_name),
	s_controller_type(controller_type),
	ctrl(NULL)
{
}

int Simulation::initialize(istream &settings)
{
	// Settings variables
	s_simulation_time = 0;
	int s_generator_seed = 0;
	int s_cars_per_hour[4] = {0};
	double s_spike_ratio[4] = {0.0};
	double s_phase_length[4] = {0.0};
	double s_phase_max_length = 0.0;
	double s_extension_step = 0.0;

	// Parse settings file
	string line;
	while (settings.good())
	{
		getline(settings, line);
		if (line.empty() || line[0] == '#')
			continue;
		string paramname;
		stringstream linestr;
		linestr.str(line);
		linestr >> paramname;
		if (paramname == "simulation_time_minutes")
			linestr >> s_simulation_time;
		else if (paramname == "generator_seed")
			linestr >> s_generator_seed;
		else if (paramname == "cars_per_hour")
			for (int i = 0; i < 4; i++)
				linestr >> s_cars_per_hour[i];
		else if (paramname == "cars_per_hour")
			for (int i = 0; i < 4; i++)
				linestr >> s_cars_per_hour[i];
		else if (paramname == "spike_ratio")
			for (int i = 0; i < 4; i++)
				linestr >> s_spike_ratio[i];
		else if (paramname == "phase_length")
			for (int i = 0; i < 4; i++)
				linestr >> s_phase_length[i];
		else if (paramname == "phase_max_length")
			linestr >> s_phase_max_length;
		else if (paramname == "extension_step")
			linestr >> s_extension_step;
		else
		{
			cout << "Error while loading settings: unknown setting: " << paramname << endl;
			return 1;
		}
	}
	if (!(s_controller_type > 0 && s_controller_type <= 3))
	{
		cout << "Error: Invalid controller type: " << s_controller_type << endl;
		return 1;
	}
	if (!s_simulation_time)
	{
		cout << "Error: Simulation time must be set!" << endl;
		return 1;
	}
	if (!s_generator_seed)
	{
		s_generator_seed = time(NULL);
	}

	// Initialize controller for the whole crossroad
	switch (s_controller_type) {
	case 1:
	{
		TimeController *tctrl = new TimeController();
		for (int i = 0; i < 4; i++)
			if(s_phase_length[i])
				tctrl->set_phase_length(i, s_phase_length[i]);
		ctrl = tctrl;
		break;
	}
	case 2:
	{
		ExtensionController *ectrl = new ExtensionController();
		for (int i = 0; i < 4; i++)
			if(s_phase_length[i])
				ectrl->set_phase_length(i, s_phase_length[i]);
		if (s_phase_max_length)
			ectrl->set_phase_max_length(s_phase_max_length);
		if (s_extension_step)
			ectrl->set_extension_step(s_extension_step);
		ctrl = ectrl;
		break;
	}
	case 3:
	{
		FuzzyController *fctrl = new FuzzyController();
		ctrl = fctrl;
		break;
	}
	}
	crossroad.add(ctrl);

	// Create and initialize all components
	for(int i=0; i<4; i++)
	{
		Generator* genr = generators[i] = new Generator(s_generator_seed+i);
		if (s_cars_per_hour[i])
			genr->set_cars_per_hour(s_cars_per_hour[i]);
		if (s_spike_ratio[i])
			genr->set_spike_ratio(s_spike_ratio[i]);
		genr->set_time_to_next_vehicle();
		RoadSensor* nearSensor = new RoadSensor();
		RoadSensor* farSensor = new RoadSensor();
		SemaphorQueue* semaphor = semaphors[i] = new SemaphorQueue(i);
		Observer* obsrv = observers[i] = new Observer();
		RoadQueue* queue1 = new RoadQueue(20, 1.5);
		RoadQueue* queue2 = queues[i] = new RoadQueue(20, 0.0);

		crossroad.add(genr);
		crossroad.add(nearSensor);
		crossroad.add(farSensor);
		crossroad.add(semaphor);
		crossroad.add(obsrv);
		crossroad.add(queue1);
		crossroad.add(queue2);

		// Couple all inputs and outputs
		crossroad.couple(genr,    genr->    out_generate,     queue2,  queue2->  in_arrive);

		crossroad.couple(queue2, queue2->out_depart, farSensor, farSensor->in_arrive);
		crossroad.couple(farSensor, farSensor->out_free, queue2, queue2->in_free);

		crossroad.couple(farSensor,   farSensor->   out_depart, queue1,queue1->in_arrive);
		crossroad.couple(queue1,   queue1->   out_free, farSensor,farSensor->in_free);

		crossroad.couple(queue1,   queue1-> out_depart,       nearSensor,nearSensor->in_arrive);
		crossroad.couple(nearSensor,   nearSensor-> out_free,       queue1,queue1->in_free);

		crossroad.couple(nearSensor,   nearSensor-> out_depart,       semaphor,semaphor->in_arrive);
		crossroad.couple(semaphor,   semaphor-> out_free,       nearSensor,nearSensor->in_free);

		crossroad.couple(farSensor,  farSensor->  out_tick_event,   ctrl,    ctrl->    in_far_sensor(i));
		crossroad.couple(nearSensor,  nearSensor->  out_tick_event,   ctrl,    ctrl->    in_near_sensor(i));
		crossroad.couple(semaphor,semaphor->out_depart,       obsrv,   obsrv->   in_departed);

		crossroad.couple(ctrl,    ctrl->    out_semaphor_cmd(i), semaphor,semaphor->in_controller_cmd);
	}
	return 0;
}

void Simulation::doSimulation()
{
	Generator::clear_counter();
	// Create a simulator and run until its done
	adevs::Simulator<IO_Type> sim(&crossroad);
	while (sim.nextEventTime() < s_simulation_time * 60.0)
	{
		sim.execNextEvent();
	}
}

void Simulation::printResults()
{
	cout << "Vysledky simulace '" << simulation_name << "' pro " << ctrl_names[s_controller_type] << endl;

	for(int i=0;i<4;i++)
	{
		double average_wait_time = observers[i]->get_total_time() / observers[i]->get_car_count();
		cout << "Strana: " << i << endl;
		cout << "\tAut projelo: " << observers[i]->get_car_count() << endl;
		cout << "\tPrumerny cas cekani: " << average_wait_time << endl;
		cout << "\tMaximalni cas cekani: " << observers[i]->get_max_time() << endl;
		cout << "\tNezarazena auta: " << queues[i]->get_denied_count() << endl;
		//cout << "\tCelkova delka zbytecne zelene: " << semaphors[i]->get_time_useless_green() << endl;
	}

	cout << endl;
}

double Simulation::getTotalAverage()
{
	double totalTime = 0;
	long totalCount = 0;
	for(int i=0;i<4;i++)
	{
		totalTime += observers[i]->get_total_time();
		totalCount += observers[i]->get_car_count();
	}

	double average_wait_time = totalTime / totalCount;
	return average_wait_time;
}

long Simulation::getTotalDenied()
{
	long totalCount = 0;
	for(int i=0;i<4;i++)
	{
		totalCount += queues[i]->get_denied_count();
	}

	return totalCount;
}